Skip to content

Commit 7f4e44a

Browse files
Small bug fixes and improvements to converter
1 parent 4ee3d73 commit 7f4e44a

File tree

3 files changed

+112
-65
lines changed

3 files changed

+112
-65
lines changed

Converter/.vscode/launch.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7-
7+
{"name":"Python Debugger: Current File","type":"debugpy","request":"launch","program":"${file}","console":"integratedTerminal"},
88
{
9-
"name": "Python Debugger: Current File",
9+
"name": "Sequence Converter Debug",
1010
"type": "debugpy",
1111
"request": "launch",
12-
"program": "./Sequence_Converter_UI.py",
13-
"console": "integratedTerminal",
14-
"subProcess": true
12+
"program": "${workspaceFolder}/Sequence_Converter_UI.py",
13+
"console": "integratedTerminal"
1514
}
1615
]
1716
}

Converter/Sequence_Converter.py

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import os
2+
import sys
23
import subprocess
34
import pymeshlab as ml
45
import numpy as np
56
from threading import Lock
6-
import threading
77
from multiprocessing.pool import ThreadPool
88
import Sequence_Metadata
99
from PIL import Image
1010

1111
class SequenceConverter:
1212

13-
isPointcloud = False
1413
terminateProcessing = False
15-
14+
debugMode = False
1615
metaData = Sequence_Metadata.MetaData()
1716

1817
modelPaths = []
@@ -25,6 +24,9 @@ class SequenceConverter:
2524
outputPath = ""
2625
resourcePath = ""
2726

27+
isPointcloud = False
28+
hasUVs = False
29+
textureDimensions = []
2830
convertToDDS = False
2931
convertToASTC = False
3032
convertToSRGB = False
@@ -35,10 +37,8 @@ class SequenceConverter:
3537
maxThreads = 8
3638
loadMeshLock = Lock()
3739
activeThreads = 0
38-
3940

4041
def start_conversion(self, model_paths_list, image_paths_list, input_path, output_path, resource_Path, processFinishedCB, threadCount, convertDDS, convertASTC, convertSRGB, decimatePointcloud, decimatePercentage):
41-
4242
self.metaData = Sequence_Metadata.MetaData()
4343
self.terminateProcessing = False
4444
self.modelPaths = model_paths_list
@@ -52,6 +52,7 @@ def start_conversion(self, model_paths_list, image_paths_list, input_path, outpu
5252
self.convertToSRGB = convertSRGB
5353
self.decimatePointcloud = decimatePointcloud
5454
self.decimatePercentage = decimatePercentage
55+
self.debugMode = False # hasattr(sys, 'gettrace') and sys.gettrace() is not None
5556

5657
modelCount = len(model_paths_list)
5758
self.metaData.headerSizes = [None] * modelCount
@@ -87,7 +88,7 @@ def finish_conversion(self, writeMetaData):
8788
self.texturePool.close()
8889
except:
8990
waitOnClose = True
90-
self.texturePool.join()
91+
self.texturePool.close()
9192

9293
if(writeMetaData):
9394
self.write_metadata()
@@ -102,15 +103,20 @@ def process_models(self):
102103
else:
103104
threads = self.maxThreads
104105

105-
self.modelPool = ThreadPool(processes = threads)
106-
self.modelPool.map_async(self.convert_model, self.modelPaths)
106+
if not self.debugMode:
107+
self.modelPool = ThreadPool(processes = threads)
108+
self.modelPool.map_async(self.convert_model, self.modelPaths)
109+
110+
else:
111+
for model in self.modelPaths:
112+
self.convert_model(model)
107113

108114
def convert_model(self, file):
109115

110116
listIndex = self.modelPaths.index(file)
111117

112118
if(self.terminateProcessing):
113-
self.convert_model_finished(False, "")
119+
self.processFinishedCB(False, "")
114120
return
115121

116122
splitted_file = file.split(".")
@@ -122,41 +128,65 @@ def convert_model(self, file):
122128

123129
ms = ml.MeshSet()
124130

125-
self.loadMeshLock.acquire() # If we don't lock the mesh loading process, crashes might occur
131+
if not self.debugMode:
132+
self.loadMeshLock.acquire() # If we don't lock the mesh loading process, crashes might occur
126133

127134
try:
128135
ms.load_new_mesh(inputfile)
129136
except:
130137
self.loadMeshLock.release()
131-
self.convert_model_finished(True, "Error opening file: " + inputfile)
138+
self.processFinishedCB(True, "Error opening file: " + inputfile)
132139
return
133140

134-
self.loadMeshLock.release()
135-
136141
if(self.terminateProcessing):
137-
self.convert_model_finished(False, "")
142+
self.processFinishedCB(False, "")
143+
self.loadMeshLock.release()
138144
return
139145

140146
faceCount = len(ms.current_mesh().face_matrix())
141-
is_pointcloud = True
142-
has_UVs = False
143147

144148
#Is the file a mesh or pointcloud?
145149
if(faceCount > 0):
146-
is_pointcloud = False
150+
pointcloud = False
151+
else:
152+
pointcloud = True
147153

148154
if(ms.current_mesh().has_wedge_tex_coord() == True or ms.current_mesh().has_vertex_tex_coord() == True):
149-
has_UVs = True
155+
uvs = True
156+
else:
157+
uvs = False
158+
159+
if(listIndex == 0):
160+
self.isPointcloud = pointcloud
161+
self.hasUVs = uvs
162+
else:
163+
if(self.hasUVs != uvs):
164+
# The sequence has different attributes, which is not allowed
165+
self.processFinishedCB(True, "Error: Some frames with UVs, some without. All frames need to be consistent with this attribute!")
166+
self.loadMeshLock.release()
167+
return
168+
if(self.isPointcloud != pointcloud):
169+
self.processFinishedCB(True, "Error: Some frames are Pointclouds, some are meshes. Mixed sequences are not allowed!")
170+
self.loadMeshLock.release()
171+
return
150172

151173
#There is a chance that the file might have wedge tex
152174
#coordinates which are unsupported in Unity, so we convert them
153175
#Also we need to ensure that our mesh contains only triangles!
154-
if(is_pointcloud == False and ms.current_mesh().has_wedge_tex_coord() == True):
155-
ms.compute_texcoord_transfer_wedge_to_vertex()
176+
if(self.isPointcloud == False and ms.current_mesh().has_wedge_tex_coord() == True):
177+
ms.compute_texcoord_transfer_wedge_to_vertex()
156178

179+
# Unity mirrors the X-Axis on import of meshes, so we need to mirror it as well
180+
# so that the axis stays consistent
181+
ms.apply_matrix_flip_or_swap_axis(flipx = True)
182+
183+
# This somehow also flips the faces, so we flip them again
184+
if(self.isPointcloud == False):
185+
ms.meshing_invert_face_orientation(forceflip = True)
157186

158187
if(self.terminateProcessing):
159-
self.convert_model_finished(False, "")
188+
self.processFinishedCB(False, "")
189+
self.loadMeshLock.release()
160190
return
161191

162192
vertices = None
@@ -165,15 +195,15 @@ def convert_model(self, file):
165195
uvs = None
166196

167197
#Load type specific attributes
168-
if(is_pointcloud == True):
198+
if(self.isPointcloud == True):
169199
vertices = ms.current_mesh().vertex_matrix().astype(np.float32)
170200
vertice_colors = ms.current_mesh().vertex_color_array()
171201

172202
else:
173203
vertices = ms.current_mesh().vertex_matrix().astype(np.float32)
174204
faces = ms.current_mesh().face_matrix()
175205

176-
if(has_UVs == True):
206+
if(self.hasUVs == True):
177207
uvs = ms.current_mesh().vertex_tex_coord_matrix().astype(np.float32)
178208

179209
vertexCount = len(vertices)
@@ -185,18 +215,21 @@ def convert_model(self, file):
185215

186216
bounds = ms.current_mesh().bounding_box()
187217

188-
if(is_pointcloud == True):
218+
if(self.isPointcloud == True):
189219
geoType = Sequence_Metadata.GeometryType.point
190220
else:
191-
if(has_UVs == False):
221+
if(self.hasUVs == False):
192222
geoType = Sequence_Metadata.GeometryType.mesh
193223
else:
194224
geoType = Sequence_Metadata.GeometryType.texturedMesh
195225

196226
if(self.terminateProcessing):
197-
self.convert_model_finished(False, "")
227+
self.processFinishedCB(False, "")
228+
self.loadMeshLock.release()
198229
return
199230

231+
if not self.debugMode:
232+
self.loadMeshLock.release()
200233

201234
#The meshlab exporter doesn't support all the features we need, so we export the files manually
202235
#to PLY with our very stringent structure. This is needed because we want to keep the
@@ -218,14 +251,14 @@ def convert_model(self, file):
218251
header += "property float y" + "\n"
219252
header += "property float z" + "\n"
220253

221-
if(is_pointcloud == True):
254+
if(self.isPointcloud == True):
222255
header += "property uchar red" + "\n"
223256
header += "property uchar green" + "\n"
224257
header += "property uchar blue" + "\n"
225258
header += "property uchar alpha" + "\n"
226259

227260
else:
228-
if(has_UVs == True):
261+
if(self.hasUVs == True):
229262
header += "property float s" + "\n"
230263
header += "property float t" + "\n"
231264
header += "element face " + str(len(faces)) + "\n"
@@ -239,7 +272,7 @@ def convert_model(self, file):
239272
f.write(headerASCII)
240273

241274
#Constructing the mesh data, as binary array
242-
if(is_pointcloud == True):
275+
if(self.isPointcloud == True):
243276

244277
verticePositionsBytes = np.frombuffer(vertices.tobytes(), dtype=np.uint8)
245278
verticeColorsBytes = np.frombuffer(vertice_colors.tobytes(), dtype=np.uint8)
@@ -267,7 +300,7 @@ def convert_model(self, file):
267300
#Vertices and UVS
268301
verticePositionsBytes = np.frombuffer(vertices.tobytes(), dtype=np.uint8)
269302

270-
if(has_UVs == True):
303+
if(self.hasUVs == True):
271304
uvsBytes = np.frombuffer(uvs.tobytes(), dtype=np.uint8)
272305

273306
verticePositionsBytes = np.reshape(verticePositionsBytes, (-1, 12))
@@ -293,13 +326,12 @@ def convert_model(self, file):
293326

294327
f.write(bytes(body))
295328

296-
self.metaData.set_metadata_Model(vertexCount, indiceCount, headerSize, bounds, geoType, has_UVs, listIndex)
329+
self.metaData.set_metadata_Model(vertexCount, indiceCount, headerSize, bounds, geoType, self.hasUVs, listIndex)
297330

298-
self.convert_model_finished(False, "")
331+
self.processFinishedCB(False, "")
299332

300-
301-
def convert_model_finished(self, error, errorText):
302-
self.processFinishedCB(error, errorText)
333+
if self.debugMode:
334+
print("Processed file: " + str(listIndex))
303335

304336
def process_images(self):
305337

@@ -309,6 +341,11 @@ def process_images(self):
309341
threads = self.maxThreads
310342

311343
self.texturePool = ThreadPool(processes= threads)
344+
345+
#Read the first image to get the dimensions
346+
self.convert_image(self.imagePaths[0])
347+
self.imagePaths.pop(0)
348+
312349
self.texturePool.map_async(self.convert_image, self.imagePaths)
313350

314351
def convert_image(self, file):
@@ -321,24 +358,28 @@ def convert_image(self, file):
321358

322359
splitted_file = file.split(".")
323360
file_name = splitted_file[0]
361+
for x in range(1, len(splitted_file) - 1):
362+
file_name += "." + splitted_file[x]
324363
inputfile = self.inputPath + "\\"+ file
325364

326365
sizeDDS = 0
327366
sizeASTC = 0
328367

329368
if(self.convertToDDS):
330369
outputfileDDS = self.outputPath + "\\" + file_name + ".dds"
331-
cmd = self.resourcePath + "texconv " + inputfile + " -o " + self.outputPath + " -m 1 -f DXT1 -y -nologo"
370+
cmd = self.resourcePath + "texconv " + "\"" + inputfile + "\"" + " -o " + "\"" + self.outputPath + "\"" +" -m 1 -f DXT1 -y -nologo"
332371
if(self.convertToSRGB):
333372
cmd += " -srgbo"
334373
if(subprocess.run(cmd).returncode != 0):
335374
self.processFinishedCB(True, "Error converting DDS texture: " + inputfile)
375+
return
336376

337377
if(self.convertToASTC):
338378
outputfileASCT = self.outputPath + "\\" + file_name + ".astc"
339-
cmd = self.resourcePath + "astcenc -cl " + inputfile + " " + outputfileASCT + " 6x6 -medium -silent"
379+
cmd = self.resourcePath + "astcenc -cl " + "\"" + inputfile + "\"" + " " + "\"" + outputfileASCT + "\"" + " 6x6 -medium -silent"
340380
if(subprocess.run(cmd).returncode != 0):
341381
self.processFinishedCB(True, "Error converting ASTC texture: " + inputfile)
382+
return
342383

343384
# Write the metadata once per sequence
344385
if(listIndex == 0):
@@ -352,10 +393,17 @@ def convert_image(self, file):
352393
if(len(self.imagePaths) > 1):
353394
textureMode = Sequence_Metadata.TextureMode.perFrame
354395

396+
self.textureDimensions = self.get_image_dimensions(inputfile)
397+
self.metaData.set_metadata_texture(self.convertToDDS, self.convertToASTC, self.textureDimensions[0], self.textureDimensions[1], sizeDDS, sizeASTC, textureMode)
398+
else:
355399
dimensions = self.get_image_dimensions(inputfile)
356-
self.metaData.set_metadata_texture(self.convertToDDS, self.convertToASTC, dimensions[0], dimensions[1], sizeDDS, sizeASTC, textureMode)
357-
358-
400+
if len(dimensions) < 2:
401+
self.processFinishedCB(True, "Could not get image dimensions!")
402+
return
403+
if(dimensions[0] != self.textureDimensions[0] or dimensions[1] != self.textureDimensions[1]):
404+
self.processFinishedCB(True, "All textures need to have the same resolution! Frame " + str(listIndex))
405+
return
406+
359407
self.processFinishedCB(False, "")
360408

361409
def get_image_dimensions(self, filePath):

0 commit comments

Comments
 (0)