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

Support baking the mesh on editor export to glft #525

Open
scottdavis opened this issue Oct 13, 2024 · 3 comments
Open

Support baking the mesh on editor export to glft #525

scottdavis opened this issue Oct 13, 2024 · 3 comments

Comments

@scottdavis
Copy link

Description

Currently if you export a scene with a Terrian3D node it exports everything except the Terrian3D mesh. This would be super helpful for using 3rd party navmesh libraries and blender workflows.

Currently i have a workflow where I dump the baked mesh to an obj file then import it into blender and merge the gltf export. Its super time consuming.

Hoping there is a callback that can bake the mesh on export when the editor makes the gltf.

if anyone happens to stumble on this needing a solutions my work around involves baking the mesh then passing it to this function.

func export_mesh_to_obj(mesh: Mesh, filepath: String):
	if mesh == null:
		print("No mesh found")
		return

	var file = FileAccess.open(filepath, FileAccess.ModeFlags.WRITE)
	if file == null:
		print("Failed to open file for writing")
		return

	# Write the OBJ header
	file.store_line("# Exported Mesh OBJ\n")

	# Prepare MeshDataTool
	var mesh_data_tool: MeshDataTool = MeshDataTool.new()

	# Iterate through surfaces of the mesh
	for surface_index in range(mesh.get_surface_count()):
		if mesh.surface_get_primitive_type(surface_index) != Mesh.PRIMITIVE_TRIANGLES:
			print("Surface is not made of triangles, skipping.")
			continue

		# Create MeshDataTool from the surface
		if mesh_data_tool.create_from_surface(mesh, surface_index) != OK:
			print("Failed to create MeshDataTool from surface")
			continue

		# Write vertices
		var vertex_count = mesh_data_tool.get_vertex_count()
		for i in range(vertex_count):
			var vertex = mesh_data_tool.get_vertex(i)
			file.store_line("v %f %f %f" % [vertex.x, vertex.y, vertex.z])

		# Write faces (OBJ uses 1-based indexing, so we add +1 to the indices)
		var face_count = mesh_data_tool.get_face_count()
		for i in range(face_count):
			var i1 = mesh_data_tool.get_face_vertex(i, 0) + 1
			var i2 = mesh_data_tool.get_face_vertex(i, 1) + 1
			var i3 = mesh_data_tool.get_face_vertex(i, 2) + 1
			file.store_line("f %d %d %d" % [i1, i2, i3])

	# Close the file after writing
	file.close()
	print("Mesh exported successfully to: ", filepath)

func export_terrain_to_obj(terrain: Terrain3D, filepath: String):
	var file = FileAccess.open(filepath, FileAccess.ModeFlags.WRITE)
	if file == null:
		print("Failed to open file for writing")
		return

	# Write the OBJ header
	file.store_line("# Exported Terrain3D OBJ\n")

	# Get terrain meshes (each chunk of terrain is handled separately)
	var meshes = terrain.get_meshes()  # Get the meshes for the terrain chunks
	for mesh_data in meshes:
		var mesh = mesh_data.mesh

		if mesh == null:
			continue

		var mesh_data_tool = MeshDataTool.new()

		# Iterate through all surfaces in the mesh
		for surface_index in range(mesh.get_surface_count()):
			if mesh.surface_get_primitive_type(surface_index) != Mesh.PRIMITIVE_TRIANGLES:
				print("Surface is not made of triangles, skipping.")
				continue

			if mesh_data_tool.create_from_surface(mesh, surface_index) != OK:
				print("Failed to create MeshDataTool from surface")
				continue

			# Write vertices
			var vertex_count = mesh_data_tool.get_vertex_count()
			for i in range(vertex_count):
				var vertex = mesh_data_tool.get_vertex(i)
				file.store_line("v %f %f %f" % [vertex.x, vertex.y, vertex.z])

			# Write faces (OBJ uses 1-based indexing, so we add +1 to the indices)
			var face_count = mesh_data_tool.get_face_count()
			for i in range(face_count):
				var i1 = mesh_data_tool.get_face_vertex(i, 0) + 1
				var i2 = mesh_data_tool.get_face_vertex(i, 1) + 1
				var i3 = mesh_data_tool.get_face_vertex(i, 2) + 1
				file.store_line("f %d %d %d" % [i1, i2, i3])

	# Close the file after writing
	file.close()
	print("Terrain exported successfully to: ", filepath)

Usage would look something like this

	var t3: Terrain3D = find_child("Terrain3D")
	var mesh: Mesh = t3.bake_mesh(0, Terrain3DStorage.HEIGHT_FILTER_NEAREST)
	export_mesh_to_obj(mesh, "res://test_dump.obj")
@scottdavis
Copy link
Author

After realizing that making this issue was my rubber duck i realized if you bake the mesh array then export you get the geometry. However it would still be nice it that was automatic.

@TokisanGames
Copy link
Owner

Are there any signals or function calls the editor emits when the user selects export to gltf? I didn't see any. If there aren't, we can't do anything about that.

Baking beforehand when needed is only 3-4 clicks.

Does your script do anything that our mesh baker doesn't already do? It looks like it bakes at lod0, whereas we can bake at any lod, and likely faster. Note that the terrain can be huge, up to 65.5 x 65.5km. Large terrains are not something you want to dump into text. Export to glb is more optimal than obj.

@scottdavis
Copy link
Author

Yea i ended up removing most of what my script does and doing it manually. My goal was to run it headless and generate my nav mesh via CI/CD for my server. However the closest thing i can find that the editor does would be this. https://docs.godotengine.org/en/stable/classes/class_gltfdocumentextension.html

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

No branches or pull requests

2 participants