Godot Version
4.6.2
Question
So I am currently working on making a parser for Roblox’s 1.0 mesh format in GDScript. It mostly works except that normals are inverted:
While yes this can easily be fixed by changing to front face culling, is there a way in order to reorder the vertex data in code to prevent having to change the material?
Heres the code for the special mesh (roblox 1.0 mesh file) creator:
class_name CreateSpecialMesh
extends Node
enum SPECIAL_MESH_VERSIONS {
VERSION_1,
VERSION_1_01
}
# Local function for formatting assert
static func formatAssert(function:String, error:String, params:Dictionary) -> String:
return "CreateSpecialMesh::" + function + "() " + error.format(params)
static func flipNormals(ogNormals : Vector3) -> Vector3:
return Vector3(ogNormals.x, ogNormals.y, ogNormals.z)
static func createSurfaceData(fileName: String) -> Part_SurfaceData:
var funcName = "createSurfaceData"
var surfaceData = Part_SurfaceData.new();
var meshFile = FileAccess.open(fileName, FileAccess.READ)
assert(meshFile != null, formatAssert(funcName, "Mesh file {fileName} not found!", {"fileName" : fileName}))
var meshData := meshFile.get_as_text()
meshData = meshData.replace("\n", "")
meshData = meshData.replace("\r", "")
meshData = meshData.replace("\t", "")
assert(meshData.begins_with("version 1.00") or meshData.begins_with("version 1.01"),
formatAssert(funcName, "{fileName} is not a valid 1.0 mesh file!", {"fileName" : fileName}))
var version:SPECIAL_MESH_VERSIONS = SPECIAL_MESH_VERSIONS.VERSION_1
if meshData.substr(0, 12).contains("1.01"):
version = SPECIAL_MESH_VERSIONS.VERSION_1_01
meshData = meshData.erase(0, 12)
var firstBracket:int = meshData.find("[")
var faceCount:int = int(meshData.substr(0, firstBracket))
assert(faceCount != null, formatAssert(funcName, "{fileName}'s face count is invalid!", {"fileName" : fileName}))
meshData = meshData.erase(0, firstBracket)
for i in faceCount * 3:
var pos:int = meshData.find("[")
var vertPosition:Vector3;
var vertNormals:Vector3;
var vertTexCoords:Vector2;
meshData = meshData.erase(0, pos+1)
# X Position
pos = meshData.find(",")
vertPosition.x = float(meshData.substr(0, pos))
meshData = meshData.erase(0, pos+1)
# Y Position
pos = meshData.find(",")
vertPosition.y = float(meshData.substr(0, pos))
meshData = meshData.erase(0, pos+1)
# Z Position
pos = meshData.find(",")
vertPosition.z = float(meshData.substr(0, pos))
pos = meshData.find("[")
meshData = meshData.erase(0, pos+1)
# X Normal
pos = meshData.find(",")
vertNormals.x = float(meshData.substr(0, pos))
print(vertNormals.x)
meshData = meshData.erase(0, pos+1)
# Y Normal
pos = meshData.find(",")
vertNormals.y = float(meshData.substr(0, pos))
print(vertNormals.y)
meshData = meshData.erase(0, pos+1)
# Z Normal
pos = meshData.find(",")
vertNormals.z = float(meshData.substr(0, pos))
print(vertNormals.z)
pos = meshData.find("[")
meshData = meshData.erase(0, pos+1)
# X Tex
pos = meshData.find(",")
vertTexCoords.x = float(meshData.substr(0, pos))
meshData = meshData.erase(0, pos+1)
# Y Tex
pos = meshData.find(",")
vertTexCoords.y = 1 - float(meshData.substr(0, pos))
meshData = meshData.erase(0, pos+1)
pos = meshData.find("]")
meshData = meshData.erase(0, pos+1)
if version == SPECIAL_MESH_VERSIONS.VERSION_1:
vertPosition *= 0.5
surfaceData.appendVertexData(vertPosition, flipNormals(vertNormals), vertTexCoords)
return surfaceData;
As well as the surfaceData class:
class_name Part_SurfaceData
extends Node
var vertices : PackedVector3Array;
var indices : PackedInt32Array;
var normals : PackedVector3Array;
var texCoords : PackedVector2Array;
var usesIndices : bool;
func _init(useIndices:bool = false):
print(“Creating Part Surface Data…”)
vertices = PackedVector3Array()
indices = PackedInt32Array();
normals = PackedVector3Array();
texCoords = PackedVector2Array();
usesIndices = useIndices
func appendVertexData(vertexPos:Vector3, vertexNormal:Vector3, vertexTexCoord:Vector2):
vertices.append(vertexPos)
normals.append(vertexNormal)
texCoords.append(vertexTexCoord)
func createArrayMesh() → ArrayMesh:
var surface_array =
var array_mesh : ArrayMesh = ArrayMesh.new()
surface_array.resize(Mesh.ARRAY_MAX)
surface_array[Mesh.ARRAY_VERTEX] = vertices
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_TEX_UV] = texCoords
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
return array_mesh
#surface_array[Mesh.ARRAY_INDEX] = indices
And here is a document which contains the file formats specifications if you need it: Roblox FileMesh Format Specification - Community Resources - Developer Forum | Roblox
