Add comprehensive documentation for SnapForm extension

- Created development.md for development setup and licensing information.
- Added features.md detailing functionalities and usage instructions for developers and users.
- Introduced index.md to serve as a navigation hub for the documentation.
- Developed outputs.md to explain output formats and their specifications.
- Established service.md to describe the AI Service architecture and environment setup.
- Created surface.md to outline the process of converting Image Empty to perspective meshes.
This commit is contained in:
2026-08-13 23:57:09 +08:00
parent caeb6d706c
commit 13fab9ce88
28 changed files with 998 additions and 830 deletions

View File

@@ -25,12 +25,15 @@ def input_socket(node, name, fallback):
def new_socket(interface, name, in_out, socket_type, parent=None):
return interface.new_socket(
socket = interface.new_socket(
name=name,
in_out=in_out,
socket_type=socket_type,
parent=parent,
)
if in_out == "INPUT" and hasattr(socket, "structure_type"):
socket.structure_type = "SINGLE"
return socket
def new_compare(nodes, data_type, operation, location, value):
@@ -361,8 +364,11 @@ def add_uv_reprojection(group, source, geometry, location):
def build_group(
group_name=GROUP_NAME,
include_confidence=True,
include_stretch=True,
include_discontinuity=True,
include_mask=False,
discontinuity_name="Depth Continuity",
discontinuity_attribute="o_depth_discontinuity",
discontinuity_default=50.0,
):
group = bpy.data.node_groups.new(group_name, "GeometryNodeTree")
group.is_modifier = True
@@ -381,7 +387,7 @@ def build_group(
"INPUT",
"NodeSocketFloat",
)
resolution.default_value = 0.01
resolution.default_value = 0.1
resolution.min_value = 0.000001
resolution.subtype = "DISTANCE"
remesh_iterations = new_socket(
@@ -403,22 +409,24 @@ def build_group(
smooth_amount.min_value = 0.0
smooth_amount.max_value = 1.0
smooth_amount.subtype = "FACTOR"
if include_mask:
mask_socket = new_socket(
interface,
"Mask",
"INPUT",
"NodeSocketBool",
)
mask_socket.default_value = True
mask_socket.structure_type = "SINGLE"
culling_panel = None
if include_confidence or include_stretch:
if include_confidence or include_discontinuity or include_mask:
culling_panel = interface.new_panel(
name="Culling",
default_closed=False,
)
if include_discontinuity:
discontinuity = new_socket(
interface,
discontinuity_name,
"INPUT",
"NodeSocketFloat",
parent=culling_panel,
)
discontinuity.default_value = discontinuity_default
discontinuity.min_value = 0.0
discontinuity.max_value = 100.0
discontinuity.subtype = "PERCENTAGE"
if include_confidence:
confidence = new_socket(
interface,
@@ -431,18 +439,15 @@ def build_group(
confidence.min_value = 0.0
confidence.max_value = 100.0
confidence.subtype = "PERCENTAGE"
if include_stretch:
edge = new_socket(
if include_mask:
mask_socket = new_socket(
interface,
"Stretch",
"Mask",
"INPUT",
"NodeSocketFloat",
"NodeSocketBool",
parent=culling_panel,
)
edge.default_value = 10.0
edge.min_value = 0.0
edge.max_value = 100.0
edge.subtype = "PERCENTAGE"
mask_socket.default_value = False
options_panel = interface.new_panel(
name="Options",
@@ -470,9 +475,13 @@ def build_group(
(-1800.0, 800.0),
{"Confidence"},
)
stretch_input = None
if include_stretch:
stretch_input = new_group_input(nodes, (-1800.0, 50.0), {"Stretch"})
discontinuity_input = None
if include_discontinuity:
discontinuity_input = new_group_input(
nodes,
(-1800.0, 50.0),
{discontinuity_name},
)
remesh_input = new_group_input(
nodes,
(-100.0, -500.0),
@@ -524,54 +533,67 @@ def build_group(
geometry = delete_confidence.outputs["Geometry"]
culled_geometry = geometry
if include_stretch:
edge_attribute = nodes.new("GeometryNodeInputNamedAttribute")
edge_attribute.data_type = "FLOAT"
edge_attribute.inputs["Name"].default_value = "o_edge_stretch"
edge_attribute.location = (-1800.0, 350.0)
edge_scale = nodes.new("ShaderNodeMath")
edge_scale.operation = "MULTIPLY"
edge_scale.inputs[1].default_value = 0.01
edge_scale.location = (-1600.0, -50.0)
edge_compare = new_compare(
if include_discontinuity:
discontinuity_data = nodes.new("GeometryNodeInputNamedAttribute")
discontinuity_data.data_type = "FLOAT"
discontinuity_data.inputs["Name"].default_value = discontinuity_attribute
discontinuity_data.location = (-1800.0, 350.0)
culling_scale = nodes.new("ShaderNodeMath")
culling_scale.operation = "MULTIPLY"
culling_scale.inputs[1].default_value = 0.01
culling_scale.location = (-1650.0, -50.0)
inverse_culling = nodes.new("ShaderNodeMath")
inverse_culling.operation = "SUBTRACT"
inverse_culling.inputs[0].default_value = 0.5
inverse_culling.location = (-1450.0, -50.0)
exponent_scale = nodes.new("ShaderNodeMath")
exponent_scale.operation = "MULTIPLY"
exponent_scale.inputs[1].default_value = 10.0
exponent_scale.location = (-1250.0, -50.0)
threshold_power = nodes.new("ShaderNodeMath")
threshold_power.operation = "POWER"
threshold_power.inputs[0].default_value = 2.0
threshold_power.location = (-1050.0, -50.0)
threshold_scale = nodes.new("ShaderNodeMath")
threshold_scale.operation = "MULTIPLY"
threshold_scale.inputs[1].default_value = 0.04
threshold_scale.location = (-850.0, -50.0)
discontinuity_compare = new_compare(
nodes,
"FLOAT",
"GREATER_THAN",
(-1200.0, 350.0),
(-650.0, 350.0),
0.0,
)
links.new(stretch_input.outputs["Stretch"], edge_scale.inputs[0])
links.new(edge_attribute.outputs["Attribute"], input_socket(edge_compare, "A", 0))
links.new(edge_scale.outputs[0], input_socket(edge_compare, "B", 1))
links.new(
discontinuity_input.outputs[discontinuity_name],
culling_scale.inputs[0],
)
links.new(culling_scale.outputs[0], inverse_culling.inputs[1])
links.new(inverse_culling.outputs[0], exponent_scale.inputs[0])
links.new(exponent_scale.outputs[0], threshold_power.inputs[1])
links.new(threshold_power.outputs[0], threshold_scale.inputs[0])
links.new(
discontinuity_data.outputs["Attribute"],
input_socket(discontinuity_compare, "A", 0),
)
links.new(
threshold_scale.outputs[0],
input_socket(discontinuity_compare, "B", 1),
)
delete_geometry = nodes.new("GeometryNodeDeleteGeometry")
delete_geometry.domain = "EDGE"
delete_geometry.domain = "FACE"
delete_geometry.location = (-350.0, 100.0)
links.new(geometry, delete_geometry.inputs["Geometry"])
links.new(edge_compare.outputs["Result"], delete_geometry.inputs["Selection"])
links.new(
discontinuity_compare.outputs["Result"],
delete_geometry.inputs["Selection"],
)
culled_geometry = delete_geometry.outputs["Geometry"]
vertex_neighbors = nodes.new("GeometryNodeInputMeshVertexNeighbors")
vertex_neighbors.location = (-550.0, -320.0)
orphan_vertex = new_compare(
nodes,
"INT",
"EQUAL",
(-350.0, -320.0),
0,
)
links.new(
vertex_neighbors.outputs["Face Count"],
input_socket(orphan_vertex, "A", 0),
)
delete_orphans = nodes.new("GeometryNodeDeleteGeometry")
delete_orphans.domain = "POINT"
delete_orphans.location = (-300.0, 100.0)
links.new(culled_geometry, delete_orphans.inputs["Geometry"])
links.new(orphan_vertex.outputs["Result"], delete_orphans.inputs["Selection"])
remeshed = add_remesh_zone(
group,
delete_orphans.outputs["Geometry"],
culled_geometry,
remesh_input.outputs["Resolution"],
remesh_input.outputs["Relax Iterations"],
topology_input.outputs["Fix Topology"],
@@ -634,7 +656,11 @@ def build_group(
def configure_moge_surface_group(group):
nodes = group.nodes
links = group.links
mask_input = new_group_input(nodes, (-1800.0, 1050.0), {"Mask"})
controls = new_group_input(
nodes,
(-1800.0, 1050.0),
{"Mask"},
)
mask_attribute = nodes.new("GeometryNodeInputNamedAttribute")
mask_attribute.data_type = "BOOLEAN"
mask_attribute.inputs["Name"].default_value = "o_mask"
@@ -646,7 +672,7 @@ def configure_moge_surface_group(group):
use_mask.operation = "AND"
use_mask.location = (-1150.0, 1050.0)
links.new(mask_attribute.outputs["Attribute"], invert_mask.inputs[0])
links.new(mask_input.outputs["Mask"], use_mask.inputs[0])
links.new(controls.outputs["Mask"], use_mask.inputs[0])
links.new(invert_mask.outputs[0], use_mask.inputs[1])
first_processing = min(
(
@@ -667,7 +693,7 @@ def configure_moge_surface_group(group):
links.new(use_mask.outputs[0], delete_mask.inputs["Selection"])
links.new(delete_mask.outputs["Geometry"], geometry_input)
group.description = (
"Cull MoGe-2 stretched faces, repair topology, preserve boundaries, "
"Cull MoGe-2 mask and depth-edge faces, repair topology, preserve boundaries, "
"and reproject UVs."
)
group.use_fake_user = True
@@ -1301,8 +1327,11 @@ def main():
moge_surface_group = build_group(
MOGE_SURFACE_GROUP_NAME,
include_confidence=False,
include_stretch=False,
include_discontinuity=True,
include_mask=True,
discontinuity_name="Depth Continuity",
discontinuity_attribute="o_depth_discontinuity",
discontinuity_default=50.0,
)
configure_moge_surface_group(moge_surface_group)
build_depth_plane_group()