Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[50$ Bounty] Create RealtimeCSG Brush from PolygonMesh #3

Open
Henry00IS opened this issue Sep 12, 2022 · 1 comment
Open

[50$ Bounty] Create RealtimeCSG Brush from PolygonMesh #3

Henry00IS opened this issue Sep 12, 2022 · 1 comment
Labels
bounty You will get paid if you do this help wanted Extra attention is needed

Comments

@Henry00IS
Copy link
Owner

Background

In RealtimeCSG, brushes (convex meshes) are represented using the half-edge data structure (see here).

At the moment we convert all of the polygons of a generated convex mesh into planes and then feed those into a RealtimeCSG function that converts a set of planes into a brush. But converting planes (finding the intersection point of 3 planes, where sometimes there are infinite intersections) into a convex mesh is a difficult problem. That's why it often fails and has stability problems.

Your task

Build a function that can take a PolygonMesh (see here) and build a brush in the scene, by calling
BrushFactory.CreateBrush (see here). This requires generating the half-edge connectivity data.

Files

The C# function has been prepared and only needs contents. Here is a special version of the 2D Shape Editor that you can add as a package:

com.aeternumgames.shapeeditor.zip

Here is the Bounty.cs file, place this file in your RealtimeCSG installation (in your Assets directory):

RealtimeCSGBounty.zip

Try extruding using "RealtimeCSG -> Create Bevel" and you will get a notification in the console window that leads you to the function that needs to be written.

public static CSGBrush CreateBrushFromPolygonMesh(Transform parent, string brushName, PolygonMesh mesh)
{
	var controlMesh = new ControlMesh();
	var shape = new Shape();

	Debug.Log("Bounty code goes here! Thank you! <3");
	return BrushFactory.CreateBrush(parent, brushName, controlMesh, shape);
}

Tips

  • A PolygonMesh is just a List of Polygon. They are convex n-gons (no triangulation) without duplicate normals.
  • The payment of 50$ (or 50€ if preferred) will be made via PayPal after verifying your work.
@Henry00IS Henry00IS added the help wanted Extra attention is needed label Sep 12, 2022
@Henry00IS
Copy link
Owner Author

This is a functional prototype / proof of concept of Bounty.cs written by me. But it's not perfect, it will fail when the front or back scale of a "scaled extrude" is zero (a pyramid shape). It's also pretty hacky with LINQ.

using AeternumGames.ShapeEditor;
using InternalRealtimeCSG;
using RealtimeCSG.Components;
using RealtimeCSG.Legacy;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Shape = RealtimeCSG.Legacy.Shape;

namespace RealtimeCSG
{
    public static class Bounty
    {
        public static CSGBrush CreateBrushFromPolygonMesh(Transform parent, string brushName, PolygonMesh mesh)
        {
            foreach (var polygon in mesh)
            {
                polygon.Reverse();
            }

            var controlMesh = new ControlMesh();

            //// control mesh polygons:

            int edgeIndex = 0;
            controlMesh.Polygons = new Legacy.Polygon[mesh.Count];
            for (int i = 0; i < mesh.Count; i++)
            {
                controlMesh.Polygons[i] = new Legacy.Polygon(new int[mesh[i].Count], i);
                for (int j = 0; j < mesh[i].Count; j++)
                {
                    controlMesh.Polygons[i].EdgeIndices[j] = edgeIndex;
                    edgeIndex++;
                }
            }

            //// control mesh vertices:

            var vertices = new List<Vector3>();
            foreach (var polygon in mesh)
            {
                foreach (var vertex in polygon)
                {
                    vertices.Add(vertex.position);
                }
            }
            controlMesh.Vertices = vertices.Distinct().ToArray();

            //// control mesh half edges:

            var edges = new List<HalfEdge>();
            for (short i = 0; i < mesh.Count; i++)
            {
                var polygon = mesh[i];
                for (int j = 0; j < polygon.Count; j++)
                {
                    var vertex = polygon[j];
                    var vertexIndex = (short)vertices.FindIndex(v => v.EqualsWithEpsilon5(vertex.position));
                    edges.Add(new HalfEdge { PolygonIndex = i, VertexIndex = vertexIndex, TwinIndex = -1 });
                }
            }
            controlMesh.Edges = edges.ToArray();

            //// control mesh half edge twin association:

            var twn = 0;
            for (short i = 0; i < mesh.Count; i++)
            {
                var polygon = mesh[i];
                for (int j = 0; j < polygon.Count; j++)
                {
                    var previous = polygon.PreviousVertex(j);
                    var current = polygon[j];

                    var cnt = 0;
                    for (short z = 0; z < mesh.Count; z++)
                    {
                        var polygon2 = mesh[z];

                        for (int k = 0; k < polygon2.Count; k++)
                        {
                            cnt++;
                            if (i == z) continue;
                            var previous2 = polygon2.PreviousVertex(k);
                            var current2 = polygon2[k];

                            if (previous2.position.EqualsWithEpsilon5(current.position) && current2.position.EqualsWithEpsilon5(previous.position))
                            {
                                controlMesh.Edges[twn].TwinIndex = cnt - 1;
                            }
                        }
                    }

                    twn++;
                }
            }

            //// shape:

            var shape = new Shape();
            var material = CSGSettings.DefaultMaterial;
            var polygonCount = mesh.Count;

            shape.Surfaces = new Surface[polygonCount];
            shape.TexGens = new TexGen[polygonCount];
            shape.TexGenFlags = new TexGenFlags[polygonCount];
            for (int i = 0; i < polygonCount; i++)
            {
                shape.Surfaces[i].TexGenIndex = i;
                shape.Surfaces[i].Plane = GeometryUtility.CalcPolygonPlane(controlMesh, (short)i);
                GeometryUtility.CalculateTangents(shape.Surfaces[i].Plane.normal, out shape.Surfaces[i].Tangent, out shape.Surfaces[i].BiNormal);
                shape.TexGens[i].RenderMaterial = material;
                shape.TexGens[i].Scale = MathConstants.oneVector3;
                shape.TexGenFlags[i] = CSGSettings.DefaultTexGenFlags;
            }

            ShapeUtility.EnsureInitialized(shape);
            controlMesh.Valid = ControlMeshUtility.Validate(controlMesh, shape);

            if (controlMesh.Valid)
            {
                Debug.Log("The control mesh is valid!");
            }

            return BrushFactory.CreateBrush(parent, brushName, controlMesh, shape);
        }
    }
}

@Henry00IS Henry00IS added the bounty You will get paid if you do this label Sep 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bounty You will get paid if you do this help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant