如何在 Godot 中制作 PLY 的解析器:Triangle Fan
How to make a parser of PLY in Godot: Triangle Fan
我一直在为 Godot 中的 3D 模型创建着色工具,但我需要能够将 PLY 格式转换为 Godot 中的 SurfaceTool。 PLY格式有一个header说明结构是什么,然后给出数据。这是立方体的示例:
ply
format ascii 1.0
comment made by Greg Turk
comment this file is a cube
element vertex 8
property float x
property float y
property float z
element face 6
property list uchar int vertex_index
end_header
0 0 0
0 0 1
0 1 1
0 1 0
1 0 0
1 0 1
1 1 1
1 1 0
4 0 1 2 3
4 7 6 5 4
4 0 4 5 1
4 1 5 6 2
4 2 6 7 3
4 3 7 4 0
我已经将顶点和面信息放入不同的数组中 header。我创建了这样的网格:
var meshBuilder = SurfaceTool.new()
meshBuilder.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
我添加了这样的顶点:
for i in range(vertex_count):
meshBuilder.add_normal(Vector3.UP)
meshBuilder.add_vertex(Vector3(float(data[0]),
float(data[1]),
float(data[2])))
line = loadedFile.get_line()
if line == "":
break
data = line.split(" ")
但是我不知道怎么加人脸。它是一个三角形扇形,其中第一个面 4 0 1 2 3 是 4 个顶点,由三角形 0,1,2 和 0,2,3 而不是 0,1,2 和 1,2,3 组成。这是使用顶点的顺序。
也许我应该按照面部所说的确切顺序添加顶点,但我不知道如何使用 add_uv,因为有人告诉我要使用它。
首先,不要将顶点直接添加到SurfaceTool
。您将把顶点存储在一个数组中,然后根据您从文件中读取的面添加它们。
其次,在 PLY 格式中,每个 face 是一个 Triangle Fan。 整体不是三角扇。此外,SurfaceTool
不会让你一次创建多个三角扇(第二次调用begin
内部调用clear
).因此,我们需要在创建每个三角形扇时提交它们。
而且,顺便说一下,您的示例中没有 UV(纹理坐标)。
让我们从将顶点存储到数组开始(假设您刚刚读到“end_header”):
var vertex_array:Array = []
for vertex_index in vertex_count:
line = loadedFile.get_line()
data = line.split(" ")
var vertex := Vector3(float(data[0]), float(data[1]), float(data[2]))
vertex_array.append(vertex)
然后我们需要读脸:
var mesh := ArrayMesh.new()
var mesh_builder := SurfaceTool.new()
for face_index in faces_count:
line = loadedFile.get_line()
data = line.split(" ")
mesh_builder.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
for face_vertex_index in range(1, data.size()):
mesh_builder.add_vertex(vertex_array[int(data[face_vertex_index])])
mesh_builder.commit(mesh)
请注意,我确实放弃了处理面中的顶点数 (data[0]
)。相反,我使用 range(1, data.size())
跳过它(如果我们包含它,范围将从 0
而不是 1
开始)。 data
中的其他值是我们存储顶点的数组的索引。
所以 4 0 1 2 3
意味着我需要从位于 0
、1
、2
和 3
位置的数组中获取顶点(正如我所说,我确实放弃了处理 4
)。
设置法线需要额外的工作。我们需要分别处理每个三角形(不是每个三角形扇形),并计算法线。
法线是垂直于三角形的向量,我们可以通过顶点差的叉积来计算。这意味着我们需要构成每个三角形的一组顶点来计算它们的法线。
如果我们要分别处理每个三角形,我们不妨使用PRIMITIVE_TRIANGLES
并使用generate_normals
(只有在使用PRIMITIVE_TRIANGLES
时才有效):
var mesh := ArrayMesh.new()
var mesh_builder := SurfaceTool.new()
for face_index in faces_count:
line = loadedFile.get_line()
data = line.split(" ")
mesh_builder.begin(Mesh.PRIMITIVE_TRIANGLES)
for triangle_index in range(2, data.size() - 1):
mesh_builder.add_vertex(vertex_array[int(data[1])])
mesh_builder.add_vertex(vertex_array[int(data[triangle_index])])
mesh_builder.add_vertex(vertex_array[int(data[triangle_index + 1])])
surface_tool.generate_normals()
mesh_builder.commit(mesh)
请记住,它们存储为三角扇。它们都共享第一个顶点 (data[1]
)。因此,在 4 0 1 2 3
中,三角形共享存储在数组中位置 0
.
的顶点
我们可以遍历 data
以获得第二个顶点。这意味着这次我们需要跳过两个元素(4
和 0
),因此 range(2, data.size() - 1)
.
中的 2
最后第三个顶点总是第二个之后的下一个。这也意味着我们不能迭代到最后,因此 - 1
in range(2, data.size() - 1)
.
之后,您可以将 mesh
设置为 MeshIntance
。
我一直在为 Godot 中的 3D 模型创建着色工具,但我需要能够将 PLY 格式转换为 Godot 中的 SurfaceTool。 PLY格式有一个header说明结构是什么,然后给出数据。这是立方体的示例:
ply
format ascii 1.0
comment made by Greg Turk
comment this file is a cube
element vertex 8
property float x
property float y
property float z
element face 6
property list uchar int vertex_index
end_header
0 0 0
0 0 1
0 1 1
0 1 0
1 0 0
1 0 1
1 1 1
1 1 0
4 0 1 2 3
4 7 6 5 4
4 0 4 5 1
4 1 5 6 2
4 2 6 7 3
4 3 7 4 0
我已经将顶点和面信息放入不同的数组中 header。我创建了这样的网格:
var meshBuilder = SurfaceTool.new()
meshBuilder.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
我添加了这样的顶点:
for i in range(vertex_count):
meshBuilder.add_normal(Vector3.UP)
meshBuilder.add_vertex(Vector3(float(data[0]),
float(data[1]),
float(data[2])))
line = loadedFile.get_line()
if line == "":
break
data = line.split(" ")
但是我不知道怎么加人脸。它是一个三角形扇形,其中第一个面 4 0 1 2 3 是 4 个顶点,由三角形 0,1,2 和 0,2,3 而不是 0,1,2 和 1,2,3 组成。这是使用顶点的顺序。
也许我应该按照面部所说的确切顺序添加顶点,但我不知道如何使用 add_uv,因为有人告诉我要使用它。
首先,不要将顶点直接添加到SurfaceTool
。您将把顶点存储在一个数组中,然后根据您从文件中读取的面添加它们。
其次,在 PLY 格式中,每个 face 是一个 Triangle Fan。 整体不是三角扇。此外,SurfaceTool
不会让你一次创建多个三角扇(第二次调用begin
内部调用clear
).因此,我们需要在创建每个三角形扇时提交它们。
而且,顺便说一下,您的示例中没有 UV(纹理坐标)。
让我们从将顶点存储到数组开始(假设您刚刚读到“end_header”):
var vertex_array:Array = []
for vertex_index in vertex_count:
line = loadedFile.get_line()
data = line.split(" ")
var vertex := Vector3(float(data[0]), float(data[1]), float(data[2]))
vertex_array.append(vertex)
然后我们需要读脸:
var mesh := ArrayMesh.new()
var mesh_builder := SurfaceTool.new()
for face_index in faces_count:
line = loadedFile.get_line()
data = line.split(" ")
mesh_builder.begin(Mesh.PRIMITIVE_TRIANGLE_FAN)
for face_vertex_index in range(1, data.size()):
mesh_builder.add_vertex(vertex_array[int(data[face_vertex_index])])
mesh_builder.commit(mesh)
请注意,我确实放弃了处理面中的顶点数 (data[0]
)。相反,我使用 range(1, data.size())
跳过它(如果我们包含它,范围将从 0
而不是 1
开始)。 data
中的其他值是我们存储顶点的数组的索引。
所以 4 0 1 2 3
意味着我需要从位于 0
、1
、2
和 3
位置的数组中获取顶点(正如我所说,我确实放弃了处理 4
)。
设置法线需要额外的工作。我们需要分别处理每个三角形(不是每个三角形扇形),并计算法线。
法线是垂直于三角形的向量,我们可以通过顶点差的叉积来计算。这意味着我们需要构成每个三角形的一组顶点来计算它们的法线。
如果我们要分别处理每个三角形,我们不妨使用PRIMITIVE_TRIANGLES
并使用generate_normals
(只有在使用PRIMITIVE_TRIANGLES
时才有效):
var mesh := ArrayMesh.new()
var mesh_builder := SurfaceTool.new()
for face_index in faces_count:
line = loadedFile.get_line()
data = line.split(" ")
mesh_builder.begin(Mesh.PRIMITIVE_TRIANGLES)
for triangle_index in range(2, data.size() - 1):
mesh_builder.add_vertex(vertex_array[int(data[1])])
mesh_builder.add_vertex(vertex_array[int(data[triangle_index])])
mesh_builder.add_vertex(vertex_array[int(data[triangle_index + 1])])
surface_tool.generate_normals()
mesh_builder.commit(mesh)
请记住,它们存储为三角扇。它们都共享第一个顶点 (data[1]
)。因此,在 4 0 1 2 3
中,三角形共享存储在数组中位置 0
.
我们可以遍历 data
以获得第二个顶点。这意味着这次我们需要跳过两个元素(4
和 0
),因此 range(2, data.size() - 1)
.
2
最后第三个顶点总是第二个之后的下一个。这也意味着我们不能迭代到最后,因此 - 1
in range(2, data.size() - 1)
.
之后,您可以将 mesh
设置为 MeshIntance
。