Skip to content

Added subtractive workflow #19

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

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6affd23
fix: ignore invalid editor resource paths
coen22 Jul 18, 2025
4d94b61
Ignore resource paths outside project
coen22 Jul 18, 2025
445c313
Merge pull request #1 from coen22/codex/fix-invalid-assetdatabase-paths
coen22 Jul 18, 2025
eccb27f
Add subtractive editing option to model
coen22 Jul 18, 2025
7e6b500
Updated mats version
Jul 18, 2025
ed88df3
Fix default operation model variable
coen22 Jul 18, 2025
1472931
Fix variable scope in placement tool cases
coen22 Jul 18, 2025
bd1fcaf
Add subtractive mode behavior
coen22 Jul 18, 2025
2f39c96
Implement subtractive editing by flipping normals
coen22 Jul 18, 2025
272eea1
Flip meshes when SubtractiveEditing toggled
coen22 Jul 18, 2025
246460d
Merge pull request #2 from coen22/codex/add-subtractive-editing-prope…
coen22 Jul 18, 2025
42d903f
Update README.md
coen22 Jul 18, 2025
4adf9a3
Update Wall.mat
Jul 19, 2025
6923008
Add normal smoothing support
coen22 Jul 19, 2025
6fa306e
Fix normal smoothing using custom NormalSolver
coen22 Jul 19, 2025
455869b
Update NormalSolver.cs
Jul 19, 2025
7e7c6ef
Update README.md
Jul 19, 2025
f21370c
Merge pull request #5 from coen22/codex/add-normal-smoothing-to-model…
coen22 Jul 19, 2025
6f42085
Move debug settings to dedicated inspector section
coen22 Jul 20, 2025
b32605e
Merge pull request #9 from coen22/vjpp8u-codex/add-debug-section-to-c…
coen22 Jul 20, 2025
6d6430b
Add DebugLogOutput option and logging
coen22 Jul 21, 2025
08aafca
Merge pull request #13 from coen22/p8m027-codex/add-debug-log-for-ver…
coen22 Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 99 additions & 2 deletions Components/Components/Containers/ChiselModelComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ public sealed class ChiselModelComponent : ChiselNodeComponent
public const string kCreateColliderComponentsName = nameof(CreateColliderComponents);
public const string kAutoRebuildUVsName = nameof(AutoRebuildUVs);
public const string kVertexChannelMaskName = nameof(VertexChannelMask);
public const string kSubtractiveEditingName = nameof(SubtractiveEditing);
public const string kSmoothNormalsName = nameof(SmoothNormals);
public const string kSmoothingAngleName = nameof(SmoothingAngle);
public const string kDebugLogBrushesName = nameof(DebugLogBrushes);
public const string kDebugLogOutputName = nameof(DebugLogOutput);


public const string kNodeTypeName = "Model";
Expand All @@ -235,6 +240,21 @@ public sealed class ChiselModelComponent : ChiselNodeComponent
public bool CreateColliderComponents = true;
public bool AutoRebuildUVs = true;
public VertexChannelFlags VertexChannelMask = VertexChannelFlags.All;
public bool SubtractiveEditing = false;
public bool SmoothNormals = false;
[Range(0, 180)]
public float SmoothingAngle = 45.0f;
[NonSerialized] bool prevSubtractiveEditing;
[NonSerialized] bool prevSmoothNormals;
[NonSerialized] float prevSmoothingAngle;

#region Debug
// When enabled all brush geometry will be printed out to the console
// at the start of the CSG job update
public bool DebugLogBrushes = false;
// When enabled the generated output mesh data will be printed out after rebuilding
public bool DebugLogOutput = false;
#endregion


public ChiselModelComponent() : base() { }
Expand Down Expand Up @@ -286,7 +306,25 @@ internal override CSGTreeNode RebuildTreeNodes()
var instanceID = GetInstanceID();
Node = CSGTree.Create(instanceID: instanceID);
return Node;
}
}

protected override void OnValidateState()
{
base.OnValidateState();

if (prevSubtractiveEditing != SubtractiveEditing && generated != null)
{
FlipGeneratedMeshes();
prevSubtractiveEditing = SubtractiveEditing;
}

if ((prevSmoothNormals != SmoothNormals || !Mathf.Approximately(prevSmoothingAngle, SmoothingAngle)) && generated != null)
{
SmoothGeneratedMeshes();
prevSmoothNormals = SmoothNormals;
prevSmoothingAngle = SmoothingAngle;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to do the surface flipping / normal smoothing in GenerateSurfaceTrianglesJob
Right before where ComputeTangents / ComputeUVs is performed, Since calculating a correct tangent is dependent on the normal

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think I'll close this PR and I'll create a new one where I make this more clean. But the principles work. So that's good.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool :)


public override void OnInitialize()
{
Expand Down Expand Up @@ -327,7 +365,66 @@ public override void OnInitialize()
name == ChiselModelManager.kGeneratedDefaultModelName)
IsDefaultModel = true;

IsInitialized = true;
prevSubtractiveEditing = SubtractiveEditing;
prevSmoothNormals = SmoothNormals;
prevSmoothingAngle = SmoothingAngle;
IsInitialized = true;
}

void FlipGeneratedMeshes()
{
if (generated == null)
return;

if (generated.renderables != null)
{
foreach (var renderable in generated.renderables)
if (renderable != null && renderable.sharedMesh)
ChiselMeshUtility.FlipNormals(renderable.sharedMesh);
}

if (generated.debugVisualizationRenderables != null)
{
foreach (var renderable in generated.debugVisualizationRenderables)
if (renderable != null && renderable.sharedMesh)
ChiselMeshUtility.FlipNormals(renderable.sharedMesh);
}

if (generated.colliders != null)
{
foreach (var collider in generated.colliders)
if (collider != null && collider.sharedMesh)
ChiselMeshUtility.FlipNormals(collider.sharedMesh);
}
}

void SmoothGeneratedMeshes()
{
if (generated == null)
return;

float angle = SmoothNormals ? SmoothingAngle : 0.0f;

if (generated.renderables != null)
{
foreach (var renderable in generated.renderables)
if (renderable != null && renderable.sharedMesh)
ChiselMeshUtility.SmoothNormals(renderable.sharedMesh, angle);
}

if (generated.debugVisualizationRenderables != null)
{
foreach (var renderable in generated.debugVisualizationRenderables)
if (renderable != null && renderable.sharedMesh)
ChiselMeshUtility.SmoothNormals(renderable.sharedMesh, angle);
}

if (generated.colliders != null)
{
foreach (var collider in generated.colliders)
if (collider != null && collider.sharedMesh)
ChiselMeshUtility.SmoothNormals(collider.sharedMesh, angle);
}
}

#if UNITY_EDITOR
Expand Down
31 changes: 31 additions & 0 deletions Components/Components/Generated/ChiselGeneratedObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using UnityEngine.Profiling;
using Unity.Jobs;
using UnityEngine.Pool;
using System.Text;

namespace Chisel.Components
{
Expand Down Expand Up @@ -607,6 +608,18 @@ public int FinishMeshUpdates(ChiselModelComponent model,
meshUpdates.meshDataArray = default;
Profiler.EndSample();

if (model.SubtractiveEditing)
{
for (int i = 0; i < foundMeshes.Count; i++)
ChiselMeshUtility.FlipNormals(foundMeshes[i]);
}

if (model.SmoothNormals)
{
for (int i = 0; i < foundMeshes.Count; i++)
ChiselMeshUtility.SmoothNormals(foundMeshes[i], model.SmoothingAngle);
}

// TODO: user meshDataArray data to determine if colliders are visible or not, then we can move this before the Apply
Profiler.BeginSample("UpdateColliders");
ChiselColliderObjects.UpdateProperties(model, this.colliders);
Expand Down Expand Up @@ -660,6 +673,24 @@ public int FinishMeshUpdates(ChiselModelComponent model,
colliderDebugVisualization.renderMaterials = new Material[] { ChiselProjectSettings.CollisionSurfacesMaterial };
// }}

if (model.DebugLogOutput)
{
var sb = new System.Text.StringBuilder();
sb.AppendLine("Output Mesh Debug Info:");
for (int m = 0; m < foundMeshes.Count; m++)
{
var mesh = foundMeshes[m];
sb.AppendLine($"Mesh {m} vertices:");
var verts = mesh.vertices;
for (int v = 0; v < verts.Length; v++)
sb.AppendLine($" v{v}: {verts[v]}");
var tris = mesh.triangles;
for (int t = 0; t < tris.Length; t += 3)
sb.AppendLine($" t{t / 3}: {tris[t]}, {tris[t + 1]}, {tris[t + 2]}");
}
UnityEngine.Debug.Log(sb.ToString());
}

var foundMeshCount = foundMeshes.Count;
foundMeshes.Clear();
return foundMeshCount;
Expand Down
36 changes: 36 additions & 0 deletions Components/Utility/ChiselMeshUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using UnityEngine;

namespace Chisel.Components
{
internal static class ChiselMeshUtility
{
public static void FlipNormals(Mesh mesh)
{
if (!mesh)
return;

var normals = mesh.normals;
if (normals != null && normals.Length > 0)
{
for (int i = 0; i < normals.Length; i++)
normals[i] = -normals[i];
mesh.normals = normals;
}

for (int s = 0; s < mesh.subMeshCount; s++)
{
var indices = mesh.GetTriangles(s);
Array.Reverse(indices);
mesh.SetTriangles(indices, s);
}
}

public static void SmoothNormals(Mesh mesh, float angle)
{
if (!mesh)
return;
NormalSolver.RecalculateNormals(mesh, angle);
}
}
}
2 changes: 2 additions & 0 deletions Components/Utility/ChiselMeshUtility.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading