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

Expose CSGShape3D #11602

Open
fire opened this issue Jan 18, 2025 · 2 comments
Open

Expose CSGShape3D #11602

fire opened this issue Jan 18, 2025 · 2 comments
Labels

Comments

@fire
Copy link
Member

fire commented Jan 18, 2025

Describe the project you are working on

V-Sekai is a collaborative community where we work on social VR. One of the things we've been working on is better Godot-native mesh creation.

Describe the problem or limitation you are having in your project

Godot CSG tends to make long, thin triangles. I want to write a gdextension or godot-sandbox that can extend CSGShape3D so that I can process geometry with Integer Coordinates for Intrinsic Geometry Processing, which requires manifold geometry.

Here's a standalone application version of "Integer Coordinates for Intrinsic Geometry Processing" https://github.com/V-Sekai/TOOL_mesh_intrinsic-triangulations

From the community, https://github.com/SpockBauru/CSG_Terrain/blob/main/addons/csg_terrain/csg_terrain.gd extends CSGMesh3D, which means there's an extra conversion from the internal faces API to ArrayMesh.

Image

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I want to expose CSGShape3D gdextension APIs and still looking for a good approach.

Describe how your proposal will work with code, pseudo-code, mock-ups, and/or diagrams

Expose in CSGBrush3D a process shape function similar to AnimationNodeExtension

Convert from the internal CSGBrush3D to virtual Error build_shape(PackedFloat64Array &faces, TypedVector<Material> &materials)

Run the extended operation.

Return the shape as PackedFloat64Array and TypedVector.

enum FaceElements {
	FACE_VERTEX0_X = 0,
	FACE_VERTEX0_Y,
	FACE_VERTEX0_Z,
	FACE_VERTEX1_X,
	FACE_VERTEX1_Y,
	FACE_VERTEX1_Z,
	FACE_VERTEX2_X,
	FACE_VERTEX2_Y,
	FACE_VERTEX2_Z,
	FACE_UV0_X,
	FACE_UV0_Y,
	FACE_UV1_X,
	FACE_UV1_Y,
	FACE_UV2_X,
	FACE_UV2_Y,
	FACE_AABB_POSITION_X,
	FACE_AABB_POSITION_Y,
	FACE_AABB_POSITION_Z,
	FACE_AABB_SIZE_X,
	FACE_AABB_SIZE_Y,
	FACE_AABB_SIZE_Z,
	FACE_SMOOTH,
	FACE_INVERT,
	FACE_MATERIAL,
	FACE_MAX,
};
// A conversion can look like this.
PackedFloat64Array faces;
TypedVector<Material> materials;
const int chunk_size = FACE_MAX;
const int total_faces = faces.size() / chunk_size;
for (int i = 0; i < total_faces; i++) {
	CSGBrush::Face face_data;
	const int base_index = i * chunk_size;
	for (int v = 0; v < 3; v++) {
		const int idx = base_index + v * 3;
		face_data.vertices[v] = Vector3(faces[idx + FACE_VERTEX0_X], faces[idx + FACE_VERTEX0_Y], faces[idx + FACE_VERTEX0_Z]);
		face_data.uvs[v] = Vector2(faces[idx + FACE_UV0_X], faces[idx + FACE_UV0_Y]);
	}
	face_data.smooth = (faces[base_index + FACE_SMOOTH] > 0.5f);
	face_data.invert = (faces[base_index + FACE_INVERT] > 0.5f);
	face_data.material = round(faces[base_index + FACE_MATERIAL]);
	brush_instance->faces.push_back(face_data);
}
// Fixup materials array.
process_shape(faces, materials);

If this enhancement will not be used often, can it be worked around with a few lines of script?

I am having trouble workarounding missing exposed internal methods via conversion to ArrayMesh.

It's like having a property that's a CSGMesh3D and extracting the ArrayMesh so we can process it and put it back into the CSGMesh3D as an array mesh for the next node in the csg tree.

extends MeshInstance3D

func _ready():
	await get_tree().process_frame
	var parent_node = get_parent()
	if not parent_node or not parent_node.has_method("is_root_shape") or not parent_node.is_root_shape():
		print("Error: Parent is not a CSGMesh root node.")
		return
	var meshes = parent_node.get_meshes()
	if meshes.size() > 1:
		var combined_mesh = meshes[1]
		if combined_mesh:
			replace_with_processed_mesh(parent_node, combined_mesh)
	else:
		print("Error: Not enough meshes in the root node.")

func process_array_mesh(array_mesh: ArrayMesh):
	print("Processing ArrayMesh with surface count: ", array_mesh.get_surface_count())
	var vertex_count = 0
	for i in range(array_mesh.get_surface_count()):
		var surface_array = array_mesh.surface_get_arrays(i)
		var vertices = surface_array[Mesh.ARRAY_VERTEX]
		vertex_count += vertices.size()
	print("Total number of vertices: ", vertex_count)

func replace_with_processed_mesh(root_node, processed_mesh):
	process_array_mesh(processed_mesh)
	var mesh_instance = MeshInstance3D.new()
	mesh_instance.mesh = processed_mesh
	mesh_instance.global_transform = root_node.global_transform
	if not root_node.get_parent():
		return
	root_node.get_parent().add_child(mesh_instance)
	root_node.queue_free()

Is there a reason why this should be core and not an add-on in the asset library?

It's prohibitive to make a CSGShape3D gdextension for add-ons.

@fire fire changed the title Expose CSGShape3D for gdextensions / gdscript Expose CSGShape3D Jan 18, 2025
@fire
Copy link
Member Author

fire commented Jan 18, 2025

The workaround is probably good enough, so I'll close.

@fire fire closed this as completed Jan 18, 2025
@fire fire reopened this Jan 18, 2025
@fire
Copy link
Member Author

fire commented Jan 18, 2025

The workaround doesn't process the CSGMesh and lets the CSG Tree continue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants