Is there a way to add another channel of vertex information to a GTLF import?
I’m building a VJ set with point clouds and and using vertex colors to color the points in a shader.
I also have a second channel of information in the point cloud I would like to import, but currently only one set of vertex colors is supported. (Github Proposal)
Seeing that the model is vertices only, I can’t abuse the UV coordinates and stuff the data in there, as there’s no faces.
Does anyone know of another way to get the data in there? It’s exported when I export the GLTF from Blender, but I can’t find a way to access it in Godot. Here’s a low res sample export: forest.glb (_SPLITS Channel)
If it’s at all possible with a hack I haven’t thought of, any suggestions would be helpful.
You could write a GLTFDocumentExtension that parses and modifies the ImporterMesh data with whatever data you need inside the ARRAY_CUSTOMn surface arrays.
Quick and dirty example. You should probably make it a plugin instead:
extends Node
func _ready() -> void:
# Quick way to test the changes without having to re-import the file
# You should do this in a plugin
var extension = Extension.new()
GLTFDocument.register_gltf_document_extension(extension)
# Create and load the gltf file
var doc = GLTFDocument.new()
var state = GLTFState.new()
doc.append_from_file("res://assets/forest.glb", state)
# Generate the Godot scene and add it
var node = doc.generate_scene(state)
add_child(node)
# Unregister the extension once it's not needed
GLTFDocument.unregister_gltf_document_extension(extension)
class Extension extends GLTFDocumentExtension:
func _import_node(state: GLTFState, _gltf_node: GLTFNode, _json: Dictionary, node: Node) -> Error:
if node is ImporterMeshInstance3D:
# Get the mesh and the first surface array.
var mesh = node.mesh as ImporterMesh
var array = mesh.get_surface_arrays(0)
# Quick and DIRTY way to get the "_SPLITS" accessor.
var split_accessor = int(state.json.get("meshes", [])[0].get("primitives", [])[0].get("attributes", {}).get("_SPLITS", 0))
var accessor = state.get_accessors()[split_accessor]
if accessor.accessor_type == GLTFAccessor.TYPE_SCALAR:
# Get the buffer view
var buffer_view = state.get_buffer_views()[accessor.buffer_view]
# Create a StreamPeerBuffer to convert the buffer more easily
var stream_buffer = StreamPeerBuffer.new()
# Load the buffer view data from the state
stream_buffer.data_array = buffer_view.load_buffer_view_data(state)
# Fill the buffer
var buffer = PackedFloat32Array()
while stream_buffer.get_available_bytes() > 0:
buffer.append(stream_buffer.get_float())
# Set the buffefr to the array index ARRAY_CUSTOM0
array[Mesh.ARRAY_CUSTOM0] = buffer
# Get the info of the surface as we are going to clear the mesh
var s_name = mesh.get_surface_name(0)
var s_format = mesh.get_surface_format(0)
var s_material = mesh.get_surface_material(0)
var s_type = mesh.get_surface_primitive_type(0)
# May as well change the material to our own one here
s_material = preload("res://forest_material.material")
# Clear the mesh
mesh.clear()
# And re-add the surface modifying the format flags with the our CUSTOM0 format
mesh.add_surface(s_type, array, [], {}, s_material, s_name, s_format | (Mesh.ARRAY_CUSTOM_R_FLOAT << Mesh.ARRAY_FORMAT_CUSTOM0_SHIFT))
return OK
This regression (it worked previously) was enough that I abandonded glTF for Godot. Which was a bit frustrating since I have hundreds of models that I converted to glTF expressly for Godot back in 4.2 when this worked - and they still work fine unless I reimport them.
Alllll of this is vertex based, nearly all my shaders are vertex shaders as that’s how players are allowed to customize, and that’s how the model masking works